#light

namespace CGraph.Generic.Impl

open System.Collections.Generic
open CGraph.Generic

type ActionList() =
    let m_list = new List<IAction>()
    let mutable m_reverse = false
    
    member this.Count = m_list.Count
    
    interface System.Collections.IEnumerable with
        member this.GetEnumerator() = (this :> seq<IAction>).GetEnumerator() :> System.Collections.IEnumerator
        
    interface IActionList with 
        member this.GetEnumerator() = 
            if m_reverse 
                then new ReverseListEnumerator<IAction>(m_list) :> IEnumerator<IAction>
                else m_list.GetEnumerator() :> IEnumerator<IAction>
    
        member this.Add ( act : IAction ) =
            let tryMerge ( act : IAction ) = 
                match act with
                    | :? IMergeable as mact -> 
                        if mact.Mergeable 
                            then 
                                let isMergeable (exist:IAction) =
                                    match exist with
                                        | :? IMergeable as pmact ->
                                            if ( pmact.Mergeable && pmact.MergeKey = mact.MergeKey )
                                                then Some(pmact) 
                                                else None
                                        | _ -> None
                                let firstMergeable = Seq.first isMergeable m_list
                                match firstMergeable with
                                    | Some a -> a.Merge( mact )
                                    | None -> false
                            else false
                    | _ -> false
            if not ( tryMerge( act ) )
                then if m_reverse then m_list.Insert( 0, act ) else m_list.Add(act)
                
        member this.Do() = 
            m_reverse <- false
            this |> Seq.iter (fun( a:IAction) -> a.Do())
        
        member this.Undo() = 
            m_reverse <- true
            this |> Seq.iter (fun( a:IAction) -> a.Undo())
            
        member this.OrderReversed with get() = m_reverse

    interface System.IDisposable with
        member s.Dispose() = 
            m_list 
            |> Seq.iter( fun act ->
                match act with
                    | :? System.IDisposable as act ->
                        act.Dispose()
                    | _ -> () )
            m_list.Clear()
            System.GC.SuppressFinalize s
        
        
type CollectionActionBase<'T> =
    inherit AddRemoveList<'T> as base
    /// Maintains whether we just did an undo, in which case AddedItems
    /// is really RemovedItems and vice versa.
    val mutable m_reverse : bool 
    
    new( (added:IEnumerable<'T>), (removed:IEnumerable<'T>) ) = { 
        inherit AddRemoveList<'T>( added, removed )
        m_reverse = false
    }
    abstract PerformAdd : seq<'T> -> unit
    default this.PerformAdd( item : seq<'T> ) = ()
    
    abstract PerformRemove : seq<'T> -> unit
    default this.PerformRemove(item : seq<'T>) = ()
    
    abstract Name : string with get
    default this.Name with get() = "unnamed"
    
    abstract MergeKey : obj with get
    default this.MergeKey with get() = box 0
    
    abstract Mergeable : bool with get
    default this.Mergeable with get() = false
    
    interface INamedObject with
        member this.Name with get() = this.Name
        
    member this.Do() =
        this.m_reverse <- false
        this.PerformAdd( this.m_addList )
        this.PerformRemove( this.m_removeList )
        
    member this.Undo() = 
        this.m_reverse <- true
        this.PerformAdd( this.m_removeList )
        this.PerformRemove( this.m_addList );
        
    member this.Merge( next : IMergeable ) =
        let actual = (next :?> CollectionActionBase<'T>)
        base.Merge( actual )
        true
        
    member this.AddedItems 
        with get() =
            let list = if this.m_reverse then this.m_removeList else this.m_addList
            list :> IEnumerable<'T>
            
    member this.RemovedItems 
        with get() =
            let list = if this.m_reverse then this.m_addList else this.m_removeList
            list :> IEnumerable<'T>
    
    interface IAction with
        member this.Do() = this.Do()
        member this.Undo() = this.Undo()
        
    interface IMergeable with
        member this.Merge act = this.Merge act    
        member this.MergeKey with get() = this.MergeKey
        member this.Mergeable with get() = this.Mergeable
    
    interface ICollectionAction<'T> with
        member this.AddedItems with get() = this.AddedItems
        member this.RemovedItems with get() = this.RemovedItems
        


/// This class is not a finished concept.  It is supposed to model
/// situations where you need to maintain order.
[<AbstractClass>]   
type TypedArrayRecord<'T,'A>() =
    let m_start = Dictionary<'T,'A array> ()
    let m_end = Dictionary<'T,'A array> ()
    
    member s.End with get() = m_end
    
    static member AddToDict (itemseq, dict:IDictionary<'T, 'A array >) =
        for item,value in itemseq do dict.[item] <- value
        
    member s.AddEndItems (itemseq: ('T*'A array) seq ) =
        TypedArrayRecord<'T,'A>.AddToDict( itemseq, m_end )
        
    member s.AddStartItems (itemseq: ('T*'A array) seq ) =
        TypedArrayRecord<'T,'A>.AddToDict( itemseq, m_start )
    
    abstract Name : string with get
    default this.Name with get() = "unnamed"
    
    abstract MergeKey : obj with get
    default this.MergeKey with get() = box 0
    
    abstract Mergeable : bool with get
    default this.Mergeable with get() = false
    
    /// Assumes that other happened later in time.
    member s.Merge( other:TypedArrayRecord<'T,'A> ) =
        let otherEnd =
            other.End
            |> Seq.map( fun(value)->value.Key,value.Value )
        s.AddEndItems( otherEnd )
        true
    
    interface INamedObject with
        member this.Name with get() = this.Name
        
    interface IMergeable with
        member this.Merge act = this.Merge (act :?> TypedArrayRecord<'T,'A> ) 
        member this.MergeKey with get() = this.MergeKey
        member this.Mergeable with get() = this.Mergeable
        

type PASetter<'T,'U> = delegate of 'T*'U->unit
            
/// Change a generic property on a generic object
/// Using delegate to ease integration with C#
type PropertyAction<'T,'U>(m_owner:'T,oldVal:'U,newVal:'U,m_setter:PASetter<'T,'U>,m_name:string) =
    let mutable m_newVal = newVal
    let mutable m_oldVal = oldVal
    let mutable m_current = newVal
    
    member s.NewValue with get = m_newVal and set v = m_newVal <- v
    member s.OldValue with get =  m_oldVal and set v = m_oldVal <- v
    
    member s.Merge( act:PropertyAction<'T,'U> ) =
        m_newVal <- act.NewValue
        true
    
    interface INamedObject with
        member s.Name = m_name
      
    interface IMergeable with
        member s.Merge act = s.Merge( act :?> PropertyAction<'T,'U> )
        member s.Mergeable = true
        member s.MergeKey = box (m_owner,m_name)
       
    interface IAction with
        member s.Do() = 
            m_setter.Invoke(m_owner,m_newVal)
            m_current <- m_newVal
            
        member s.Undo() =
            m_setter.Invoke(m_owner,m_oldVal)
            m_current <- m_oldVal
            
    interface IPropertyAction<'T,'U> with
        member s.Owner = m_owner
        member s.Value = m_current
        